from random import randrange
from abstractNodeKomunikacija import AbstractNodeKomunikacija
from commands import Commands
from chunkState import ChunkState
import socket
import math
import time
from __builtin__ import len
from worker import WorkerProstiBrojevi
import threading

class udpServerNode(AbstractNodeKomunikacija):
    '''WorkerProstiBrojevi worker; TREBA DODATI'''
    def __init__(self, chunkStates, requestID, opseg, listenPort, sviServeri):
        AbstractNodeKomunikacija.__init__(self, listenPort)
        self.requestID = requestID
        self.sviServeri = sviServeri
        self.brojNodovaKojeSuDodali = {} 
        '''Map<Entry<InetAddress, Integer>, Integer>'''
        
        self.prostiPoChunku = {} 
        '''Map<Integer, List<Integer>>'''
        
        self.brojNodova = sviServeri.__len__()
        self.brojNodova += 1
        self.brojNodovaKojeSamJadodao = 0
        data = opseg.split(",")
        self.pocetakOpsega = int(data[0])
        self.krajOpsega = int(data[1])
        self.brojChunkova = (self.krajOpsega - self.pocetakOpsega) / Commands.VELICINA_CHUNKA
        self.finished = False
        self.progressInfo = "Progress initializing."
        self.lastPercentages = []
        
        if ((self.krajOpsega - self.pocetakOpsega) % Commands.VELICINA_CHUNKA != 0):
            self.brojChunkova += 1
        
        if chunkStates is None:
            self.chunkStates = []
            '''ArrayList<Entry<ChunkState, Long>>'''
              
            for i in range(0, self.brojChunkova):
                self.chunkStates.append([ChunkState.NOT_DONE, 0l])
        else:
            self.chunkStates = chunkStates
            '''state i timestamp(currentMillis)''' 
        
        self.worker = WorkerProstiBrojevi()      
        self.ostaloProstihDaSePrebace = True
        print "PRAVIM NOVI SERVER NODE"
        
    def startMethod(self):
        while not self.finished:
            '''brodkastovanje server info'''
            '''for entry in self.sviServeri:
            self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_INFO, str(self.brojNodovaKojeSamJadodao))'''
            '''za sada samo broj koje sam dodao, posle u telo i ostale info'''
            '''self.requestID+=1'''
            
            '''provera da li radi poso'''
            if not self.worker.working and not self.finished:
                '''proveriti da li je prethodni zavrsen i brodkastovati da je zavrsen (mozda zajedno i sa prostimbrojevima)'''
                if self.worker.chunkId != -1:
                    timestamp = float(round(time.time() * 1000))
                    self.chunkStates[self.worker.chunkId] = [ChunkState.DONE, timestamp]
                    print self.chunkStates
                    self.prostiPoChunku.update({self.worker.chunkId:self.worker.prosti})
                    sb = ""
                    for prost in self.worker.prosti:
                        sb += ';'
                        sb += str(prost)
                    self.worker.prosti = []
                    for entry in self.sviServeri:
                        self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_FINISHED_CHUNK, str(self.worker.chunkId) + ";" + str(timestamp) + sb)
                        self.requestID += 1
                    self.worker.chunkId = -1
                    
                
                    
                '''nadji chunkove koji ne rade ili rade a proslo puno od timestampa'''
                chunksToBeDone = []
                for i in range(0, self.chunkStates.__len__()):
                    entry = self.chunkStates[i]
                    currentTime = float(round(time.time() * 1000))
                    if entry[0] == ChunkState.NOT_DONE:
                        chunksToBeDone.append(i)
                    elif entry[0] == ChunkState.IN_PROGRESS and (currentTime - long(float(entry[1])) > 600000):
                        chunksToBeDone.append(i)
                if chunksToBeDone.__len__() == 0:
                    daLiImaProgress = False
                    for entry in self.chunkStates:
                        if(entry[0] == ChunkState.IN_PROGRESS):
                            daLiImaProgress = True
                            break
                    '''Ako ih ima jos onda treba i dalje da proverava za slucaj da neki istekne'''
                    if not daLiImaProgress:
                        # print "KONACNO RESENJE: " , self.prostiPoChunku
                        self.finished = True
                    continue
                    
                '''zarandomaj koji ces'''
                chunkToDo = chunksToBeDone[randrange(chunksToBeDone.__len__())]
                
                self.worker.setChunk(chunkToDo, (chunkToDo * Commands.VELICINA_CHUNKA + 1), (chunkToDo + 1) * Commands.VELICINA_CHUNKA)
                timestamp = float(round(time.time() * 1000))
                self.chunkStates[chunkToDo] = [ChunkState.IN_PROGRESS, timestamp]
                
                '''posalji svima da radis'''
                for entry in self.sviServeri:
                    self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_STARTED_CHUNK, str(chunkToDo) + ";" + str(timestamp))
                    self.requestID += 1
           
            if not self.finished:
                countDone = 0
                for entry in self.chunkStates:
                    if entry[0] == ChunkState.DONE:
                        countDone += 1
                
                percentDone = float(countDone) / self.chunkStates.__len__()
                timestamp = float(round(time.time() * 1000))
                if([percentDone, timestamp] not in self.lastPercentages):
                    self.lastPercentages.append([percentDone, timestamp])
                #if self.lastPercentages.__len__() > 10:
                    #del self.lastPercentages[0]
                speedSum = 0
                count = 0
                for i in range(0, self.lastPercentages.__len__() - 1):
                    entryTren = self.lastPercentages[i]
                    entrySled = self.lastPercentages[i + 1]
                    speed = (entrySled[0] - entryTren[0]) / ((entrySled[1] - entryTren[1]) / 1000.0)
                    speedSum += speed
                    count += 1
                if count == 0:
                    sb = "1current done: NaN  Current speed: NaN  Estimate seconds left: INF"
                    continue
                speedMean = speedSum / count  # u procentima po sekundi
                if speedMean == 0:
                    sb = "2current done: NaN  Current speed: NaN  Estimate seconds left: INF"
                    continue
                secondsLeft = (1 - percentDone) / speedMean
                sb = "current done: "+ str(percentDone)+ " Current speed: "+ str(speedMean)+ " Estimate seconds left: "+ str(secondsLeft)
                self.progressInfo = sb
                print sb
                time.sleep(4.5)    
        while self.ostaloProstihDaSePrebace:
            if self.sviServeri.__len__() == 0:
                self.ostaloProstihDaSePrebace = False
            for entry in self.sviServeri:
                for i in range(0, self.brojChunkova):
                    sviTu = True
                    if not i in self.prostiPoChunku:
                        sviTu = False
                        
                        self.sendPaket(entry[0], entry[1], self.requestID, Commands.GIVE_ME_PRIMES, str(i))
                        time.sleep(0.03)
                    if sviTu:
                        self.ostaloProstihDaSePrebace = False
                if not self.ostaloProstihDaSePrebace:
                    break
                time.sleep(0.47)
                '''VIDETI ZA OVAJ SLEEP DA LI TREBA DA SE IZVUCE IZ FOR PETLJE'''
        print "KONACAN REZULTAT:", self.prostiPoChunku
    
    def start(self):
        self.worker.startJob()
        self.startRecieving()
        self.javiSeSvimServerima()
        
        periodicnoRadjenjeStvari = threading.Thread(target=self.startMethod, args=())
        periodicnoRadjenjeStvari.daemon = True
        periodicnoRadjenjeStvari.start()
        while True:
            try:
                time.sleep(10)
            except:
                print "Ja vise nikada necu da spavam"
            continue
        
    def javiSeSvimServerima(self):
        for entry in self.sviServeri:
            self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_HELLO, "EMPTY")
            self.requestID += 1
                
    def obradiPaket(self, recieveAddress, recievePort, requestID, komanda, telo):
        if komanda == Commands.NEW_NODE:
            sb = ';'
            sb += str(self.sviServeri.__len__() + 1)
            for entry in self.sviServeri:
                sb += ';'
                sb += entry[0]
                sb += ';'
                sb += entry[1]
            sb += ';'
            sb += socket.gethostname()
            sb += ';'
            sb += str(self.listenPort)
            role = ""
            # print self.brojNodova, " ", math.log(self.brojNodova), " ", self.sviServeri.__len__()
            if math.log(self.brojNodova) > self.sviServeri.__len__():
                role = Commands.ROLE_SERVER 
            else:
                self.brojNodovaKojeSamJadodao += 1
                self.brojNodova += 1
                role = Commands.ROLE_CLIENT
            chunkString = ';'
            chunkString += str(self.chunkStates.__len__())
            for entry in self.chunkStates:
                chunkString += ';'
                chunkString += entry[0]
                chunkString += ';'
                chunkString += str(entry[1])
            self.sendPaket(recieveAddress, recievePort, requestID, Commands.NEW_NODE_RESPONSE, role + sb + chunkString)
        elif komanda == Commands.SERVER_INFO:
            entry = [recieveAddress, recievePort]
            if entry not in self.sviServeri:
                self.sviServeri.append(entry)
            broj = int(telo)
            '''Za sada samo broj, posle mozda jos'''
            self.brojNodovaKojeSuDodali.put(entry, broj)
            self.brojNodova = 0
            for key, brojevi in self.brojNodovaKojeSuDodali.iteritems():
                self.brojNodova += brojevi
            self.brojNodova += self.brojNodovaKojeSamJadodao
            self.brojNodova += self.sviServeri.__len__() + 1
        elif komanda == Commands.SERVER_HELLO:
            '''kad se napravi novi server pa mu se javi'''
            entry = [recieveAddress, recievePort]
            if entry not in self.sviServeri:
                self.sviServeri.append(entry)
        elif komanda == Commands.SERVER_STARTED_CHUNK:
            split = telo.split(";")
            chunkId = int(split[0])
            timestamp = long(float(split[1]))
            # print self.chunkStates.__len__()
            self.chunkStates[chunkId] = [ChunkState.IN_PROGRESS, timestamp]
        elif komanda == Commands.CLIENT_STARTED_CHUNK:
            split = telo.split(";")
            chunkId = int(split[0])
            timestamp = long(split[1])
            self.chunkStates[chunkId] = [ChunkState.IN_PROGRESS, timestamp]
            '''sada ovaj mora da posaje svim serverima da je zapoceto'''
            for entry in self.sviServeri:
                self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_STARTED_CHUNK, str(chunkId) + ";" + str(timestamp))
                self.requestID += 1
        elif komanda == Commands.SERVER_FINISHED_CHUNK:
            split = telo.split(";")
            chunkId = int(split[0])
            timestamp = long(float(split[1]))
            prosti = []
            for i in range(2, len(split)):
                prost = int(split[i])
                prosti.append(prost)
            self.chunkStates[chunkId] = [ChunkState.DONE, timestamp]
            self.prostiPoChunku.update({chunkId:prosti})
        elif komanda == Commands.CLIENT_FINISHED_CHUNK:
            split = telo.split(";")
            chunkId = int(split[0])
            timestamp = long(split[1])
            prosti = []
            for i in range(2, len(split)):
                prost = int(split[i])
                prosti.append(prost)
            self.chunkStates[chunkId] = [ChunkState.DONE, timestamp]
            # print self.chunkStates
            self.prostiPoChunku.update({chunkId:prosti})
            '''sada ovaj prosledjuje svim serverima da je klijent zavrsio chunk'''
            sb = ""
            for prost in prosti:
                sb += ';'
                sb += str(prost)
            for entry in self.sviServeri:
                self.sendPaket(entry[0], entry[1], self.requestID, Commands.SERVER_FINISHED_CHUNK, str(chunkId) + ";" + str(timestamp) + sb)
                self.requestID += 1
        elif komanda == Commands.GIVE_ME_NEW_CHUNK:
            if(self.finished):
                self.sendPaket(recieveAddress, recievePort, requestID, Commands.ALL_FINISHED, "EMPTY")
            else:
                chunksToBeDone = []
                for i in range(0, self.chunkStates.__len__()):
                    entry = self.chunkStates[i]
                    currentTime = float(round(time.time() * 1000))
                    if entry[0] == ChunkState.NOT_DONE:
                        chunksToBeDone.append(i)
                    elif entry[0] == ChunkState.IN_PROGRESS and (currentTime - float(entry[1]) > 600000):
                        chunksToBeDone.append(i)
                if chunksToBeDone.__len__() == 0:
                    # print self.prostiPoChunku
                    self.sendPaket(recieveAddress, recievePort, requestID, Commands.HERE_IS_A_CHUNK, "-1")
                    '''POSLATI SVIM KLIJENTIMA prostiPoChunku da bi odstampali'''
                else:
                    chunktoDo = randrange(chunksToBeDone.__len__())
                    self.sendPaket(recieveAddress, recievePort, requestID, Commands.HERE_IS_A_CHUNK, chunksToBeDone[chunktoDo])
        elif komanda == Commands.GIVE_ME_NEW_INFO:
            sb = ';'
            sb += str(self.sviServeri.__len__() + 1)
            for entry in self.sviServeri:
                sb += ';'
                sb += str(entry[0])
                sb += ';'
                sb += str(entry[1])
            sb += ';'
            sb += socket.gethostname()
            sb += ';'
            sb += str(self.listenPort)
            
            self.sendPaket(recieveAddress, recievePort, requestID, Commands.HERE_IS_INFO, self.progressInfo+sb)
        elif komanda == Commands.GIVE_ME_PRIMES:
            chunkId = int(telo)
            try:
                prosti = self.prostiPoChunku[chunkId]
            except:
                print "OVAJ SERVER NEMA TO :D"
            if prosti is not None:  # ako nemas ove proste samo ne odgovaraj odgovorice vec neko
                sb = ""
                for prost in prosti:
                    sb += ';'
                    sb += str(prost)
                    
                self.sendPaket(recieveAddress, recievePort, requestID, Commands.HERE_ARE_PRIMES, str(chunkId) + sb)
                
                
